<?php
/*
 * Copyright (c) 2013 Eugene Kuzmenko
 *
 * Permission  is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in  the Software without restriction, including without limitation the rights
 * to  use,  copy,  modify,  merge, publish, distribute, sublicense, and/or sell
 * copies  of  the  Software,  and  to  permit  persons  to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The  above  copyright  notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE  IS  PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED,  INCLUDING  BUT  NOT  LIMITED  TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS  FOR  A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS  OR  COPYRIGHT  HOLDERS  BE  LIABLE  FOR  ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * For more information, see <http://www.aljey.com>.
 */

namespace Al\Core\Lib;

use Al\Core\Exception\FileNotFoundException;

/**
 * The AlWEB framework class loader.
 *
 * @author Eugene Kuzmenko <mail@aljey.com>
 */
class Autoload {

  /**
   * @var bool If true an exception will not be thrown when a file is not found.
   */
  public $silentFail = false;

  /**
   * Registers the autoloader.
   */
  public function register() {
    spl_autoload_register(array($this, 'load'));
  }

  /**
   * Attempts to find the physical location of the file containing the class
   * definition.
   * Uses the values from the namespaces configuration key.
   *
   * Example:
   * <code>
   * namespaces:
   *   Acme\Blog: ../src/Acme
   * </code>
   * All the paths in the application are relative to the front controller
   * (web/index.php).
   * The path defined by the namespace (eg. Acme\Blog) should point to
   * the location where the directory named as the last part of the namespace
   * (eg. Blog) is located, hence the directory structure in this example
   * would be "../src/Acme/Blog".
   *
   * Example:
   * <code>
   *   // ../src/Acme/Blog/Controller/PostController.php
   *   $service['autoload']->find('Acme\Blog\Controller\PostController');
   * </code>
   *
   * @param string $class The full class name of the class that needs to be
   *                      found.
   * @return string The file location.
   */
  public function find($class) {
    $match = '';
    $src = '../src/';
    $ns = Service::get('config.namespaces', array());
    foreach ($ns as $name => $path) {
      if ($name == '') continue;
      $part = explode('\\', $class);
      array_pop($part);
      if (preg_match('/^' . str_replace('\\', '\\\\', $name) . '/',
          implode('\\', $part)) && (strlen($name) > strlen($match))) {
        $match = $name;
        $src = $path;
      }
    }
    if (!$match && isset($ns[''])) $src = $ns[''];
    return "$src" . str_replace('\\', '/', (string)substr($class,
      strlen($match))) . '.php';
  }

  /**
   * Attempts to load a file containing the requested class definition.
   *
   * @param string $class The full class name of the class that needs to be
   *                      loaded.
   * @return boolean Has the loading been successful?
   * @throws FileNotFoundException
   */
  public function load($class) {
    if (ROOT_DIR !== getcwd()) {
      chdir(ROOT_DIR);
    }
    if (is_file($filename = $this->find($class))) {
      include $filename;
      return true;
    } else {
      if (Service::get('event')->fire('autoload.fallback', array($class))) {
        return true;
      }
      if ($this->silentFail) return false;
      throw new FileNotFoundException($filename);
    }
  }

}